package cz.cernilovsky.android.profileapp.storage;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.json.*;

import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.Geofence.Builder;

import android.content.Context;
import android.util.Log;
import cz.cernilovsky.android.profileapp.model.*;
import cz.cernilovsky.android.profileapp.services.AddGeofence;
import cz.cernilovsky.android.profileapp.services.RemoveGeofence;

public class LocationsStorage {
	public static final String FILE_NAME = "locations.json";
	public static final String NAME = "name";
	public static final String ON_ENTRY = "onEntry";
	public static final String ON_LEAVE = "onLeave";
	public static final String LATITUDE = "latitude";
	public static final String LONGITUDE = "longitude";
	public static final String RADIUS = "radius";
	public static final String ID = "id";
	private static final String TAG = "LocationsStorage";
	
	public static List<Geofence> loadGeofences(Context context) throws IOException, JSONException {
		Location[] locations = loadLocations(context);
		if(locations == null) {
			return null;
		}
		return loadGeofences(locations, context);
	}
	
	public static List<Geofence> loadGeofences(Location[] locations, Context context) throws IOException, JSONException {
		if(locations == null) {
			return null;
		}
		List<Geofence> geofences = new ArrayList<Geofence>();
		Builder builder = new Geofence.Builder();
		for(Location location : locations) {
			Geofence g = Location.toGeofence(location, builder);
			//Log.i(TAG, g.getRequestId() + "|" + g.toString());
			//Log.i(TAG, location.toString());
			geofences.add(g);
			
		}
		return geofences;
	}
	
	
	
	public static Location[] loadLocations(Context context) throws IOException, JSONException {
		BufferedReader is = null;
		try {
			is = new BufferedReader(new InputStreamReader(context.openFileInput(FILE_NAME)));
			StringBuffer sb = new StringBuffer();
			String line = null;
			while((line = is.readLine()) != null) {
				//Log.d(TAG, line);
				sb.append(line);
			}
			JSONArray json = new JSONArray(sb.toString());
			if(json.length() == 0) {
				return null;
			}
			Location[] locations = new Location[json.length()];
			Profile[] profiles = ProfilesStorage.loadProfiles(context);
			for(int i = 0; i < json.length(); i++) {
				JSONObject locationObject = json.getJSONObject(i);
				String name = locationObject.getString(NAME);
				int id = locationObject.getInt(ID);
				int radius = locationObject.getInt(RADIUS);
				double latitude = locationObject.getDouble(LATITUDE);
				double longitude = locationObject.getDouble(LONGITUDE);
				
				JSONArray onEntryJson = locationObject.getJSONArray(ON_ENTRY);
				Profile[] onEntry = new Profile[onEntryJson.length()];
				if(profiles != null) {
					out:
					for(int j = 0; j < onEntryJson.length(); j++) {
						for(Profile profile : profiles) {
							if(onEntryJson.getJSONObject(j).getInt(ID) == profile.getId()) {
								onEntry[j] = profile;
								continue out;
							}
						}
					}
				}
				
				JSONArray onLeaveJson = locationObject.getJSONArray(ON_LEAVE);
				Profile[] onLeave = new Profile[onLeaveJson.length()];
				if(profiles != null) {
					out:
					for(int j = 0; j < onLeaveJson.length(); j++) {
						for(Profile profile : profiles) {
							if(onLeaveJson.getJSONObject(j).getInt(ID) == profile.getId()) {
								onLeave[j] = profile;
								continue out;
							}
						}
					}
				}
				locations[i] = new Location(id, name, onEntry, onLeave, latitude, longitude, radius);
				System.out.println(locations[i]);
			}
			return locations;
		} catch(FileNotFoundException e) {}
		finally {
			if(is != null) {
				is.close();
			}
		}
		return null;
	}
	
	public static void deleteDependenciesOnProfile(Profile profile, Context context) throws IOException, JSONException {
		Location[] locations = loadLocations(context);
		if(locations == null)
			return;
		for(Location location : locations) {
			Profile[] cleanOnEntryProfiles = getCleanProfiles(ON_ENTRY, location, profile);
			Profile[] cleanOnLeaveProfiles = getCleanProfiles(ON_LEAVE, location, profile);
			location.setOnEntryProfiles(cleanOnEntryProfiles);
			location.setOnLeaveProfiles(cleanOnLeaveProfiles);
		}
		saveLocations(locations, context);
	}
	
	private static Profile[] getCleanProfiles(String entryOrLeave, Location location, Profile profile) {
			Profile[] testProfiles = entryOrLeave.equals(ON_ENTRY) ? location.getOnEntryProfiles() : location.getOnLeaveProfiles();
			if(testProfiles == null || testProfiles.length == 0)
				return null;
			Profile[] cleanProfiles = new Profile[testProfiles.length-1];
			for(int i = 0, j=0; i < testProfiles.length; i++) {
				Profile testProfile = testProfiles[i];
				//Log.d(TAG, "testProfile = " + testProfile);
				//Log.d(TAG, "profile = " + profile);
				if(!testProfile.equals(profile)) {
					cleanProfiles[j++] = testProfile;
				}
			}
			return cleanProfiles;
	}
	
	public static void addLocation(Location location, Context context) throws IOException, JSONException {
		Location[] locations = loadLocations(context);
		Location[] locations1;
		if(locations != null) {
			//locations1 = Arrays.copyOf(locations, locations.length+1);
			locations1 = new Location[locations.length+1];
			for(int i = 0; i < locations.length; i++) {
				locations1[i] = locations[i];
			}
			location.setId(getHighestId(locations)+1);
			locations1[locations.length] = location;
		} else {
			location.setId(0);
			locations1 = new Location[] {location};
		}
		saveLocations(locations1, context);
		AddGeofence.addLocation(location, context);
	}
	
	private static int getHighestId(Location[] locations) {
		int max = 0;
		for(Location location : locations) {
			max = Math.max(location.getId(), max);
		}
		return max;
	}

	public static void editLocation(Location location, Context context) throws IOException, JSONException {
		Location[] locations = loadLocations(context);
		for(int i = 0; i < locations.length; i++) {
			if(locations[i].getId() == location.getId()) {
				locations[i] = location;
				saveLocations(locations, context);
				AddGeofence.addLocation(location, context);
				return;
			}
		}
	}
	
	public static void deletelocation(Location location, Context context) throws IOException, JSONException {
		Location[] locations = loadLocations(context);
		Location[] locations1 = new Location[locations.length-1];
		for(int i = 0,j=0; i < locations.length; i++) {
			if(locations[i].equals(location)) {
				continue;
			}
			locations1[j++] = locations[i];
		}
		saveLocations(locations1, context);
		RemoveGeofence.removeLocation(location, context);
	}

	private static void saveLocations(Location[] locations, Context context) throws JSONException, IOException {
		JSONArray array = new JSONArray();
		for(Location location : locations) {
			JSONObject object = new JSONObject();
			object.put(NAME, location.getName());
			object.put(ID, location.getId());
			object.put(LATITUDE, location.getLatitude());
			object.put(LONGITUDE, location.getLongitude());
			object.put(RADIUS, location.getRadius());
			
			JSONArray onEntry = new JSONArray();
			if(location.getOnEntryProfiles() != null) {
				for(Profile profile : location.getOnEntryProfiles()) {
					JSONObject obj = new JSONObject();
					obj.put(ID, profile.getId());
					onEntry.put(obj);
				}
			}
			object.put(ON_ENTRY, onEntry);
			
			JSONArray onLeave = new JSONArray();
			if(location.getOnLeaveProfiles() != null) {
				for(Profile profile : location.getOnLeaveProfiles()) {
					JSONObject obj = new JSONObject();
					obj.put(ID, profile.getId());
					onLeave.put(obj);
				}
			}
			object.put(ON_LEAVE, onLeave);
			
			array.put(object);
		}
		BufferedWriter bw = null;
		try {
			bw = new BufferedWriter(new OutputStreamWriter(context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE)));
			bw.write(array.toString());
			bw.flush();
		} finally {
			if(bw != null)
				bw.close();
		}
	}
}
